package compiler.common;

import java.util.List;
import java.util.ArrayList;

/**
 * Method class represents a method in JKlasy object
 * @author Michal Anglart
 */

public class Method {
  private String name;
  private List<String> argtypes;  // names of arguments types
  private String rettype;         // name of return type
  private boolean modifier;       // true if method is public, false otherwise
  private boolean operator;       // true if method is an operator, false otherwise
  private boolean implemented;    // true if method have implementation
  
  /**
   * Standard constructor 
   * @param name Name of method
   */
  public Method(String name) {
    this.name = name;
    this.argtypes = new ArrayList<String>();
  }
  
  /**
   * Sets modifier value
   * @param modifier
   */
  public void setModifier(boolean modifier) {
    this.modifier = modifier;
  }
  
  /**
   * Gets modifier value
   */
  public boolean getModifier() {
    return this.modifier;
  }
  
  /**
   * Sets operator value
   * @param operator
   */
  public void setOperator(boolean operator) {
    this.operator = operator;
  }

  /**
   * Gets operator value
   */
  public boolean getOperator() {
    return this.operator;
  }
    
  /**
   * Sets operator value
   * @param implemented
   */
  public void setImplementation(boolean impl) throws Exception {
    if(this.implemented)
      throw new Exception("Double implemented method '" + this.name + "'");
    else
      this.implemented = impl;
  }

  /**
   * Gets implemented value
   */
  public boolean getImplementation() {
    return this.implemented;
  }
    
  /** Gets method name
   * @return Name of method
   */
  public String getName() {
    return this.name;
  }
  
  /**
   * Gets arguments types
   * @return List of types
   */
  public List<String> getArgTypes() {
    return this.argtypes;
  }
  
  /**
   * Adds parameter type name to list of method arguments types
   * @param typename Name of type
   */
  public void addArgType(String typename) {
    this.argtypes.add(typename);
  }

  /**
   * Sets return type of method
   * @param typename Name of type
   */
  public void setRetType(String typename) {
      this.rettype = typename;
  }
  
  /**
   * Gets return type (or special value #void if method doesn't return any value)
   * @return Type
   */
  public String getRetType() {
    if(this.rettype == null)
      return "#void";
    else
      return this.rettype;
  }
  
  /**
   * Gets method arity
   * @return method arity
   */
  public int arity() {
    return this.argtypes.size();
  }
  
  /**
   * Test if two instances of Method class has the same name, and arity
   * 
   * it doesn't check any other fields, and it shouldn't !!
   */
  public boolean equals(Object o) {
    if(o == null) {
      return false;
    }
    
    if(o instanceof Method) {
      Method obj = (Method) o;
      
      // we make sure the method names match
      if (!this.name.equals(obj.name)) {
        return false;
      }
      
      // we make sure the arity is equal
      if (obj.arity() != this.arity()) {
        return false;
      }
     
      // everything is fine!
      return true; 
    } 
    
    // compared object is not a instance of Method
    return false;
  }
  
  private String makeEmiterName(String type, boolean sd) {
    String ret = "";
    
    if(type == null)
      return "V";
    
    if(type.startsWith("array of ")) {
      type=type.substring(9);
      ret = "[";
    }
    if(type == "integer")
      ret += "I";
    else if(type == "boolean")
      ret += "Z";
    else if(type == "char")
      ret += "C";
    else if(type == "real" && sd)
      ret += "D";
    else if(type == "real" && !sd)
      ret += "F";
    else if(type == "string")
      ret += "Ljava/lang/String;";
    else
      ret += "L" + type + ";";
    
    return ret;
  }
  
  public String getEmiterSignature(String classname, boolean fd) {
    String sig = "";
    String args = "";
    String ret = "";
    
    for(String arg : this.argtypes) 
      args += this.makeEmiterName(arg, fd);
    
    ret = this.makeEmiterName(this.rettype, fd);
    
    if(classname.equals(this.name))
      sig = "<init>(" + args + ")" + ret;
    else
      sig = this.name + "(" + args + ")" + ret;
    return sig;
  }

  /**
   * Test if this is a method of given name and type parametres
   * @param name
   * @param types
   * @return
   */
  public boolean isEqual(String methodname, List<String> types) {
    return  this.name.equals(methodname) && this.argtypes.equals(types);
  }
}

